/* Copyright - Apache License 2.0
 * 
 * The project "kyou" is
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.kyou.util;

import java.nio.charset.Charset;

import net.kyou.exception.KyouErr;
import net.kyou.exception.KyouException;

/**
 * 工具类 用来解析KyouString
 * 
 * @author NuclearG (<a href="mailto:nuclearg@163.com">nuclearg@163.com</a>)
 */
public class KyouString {
    /**
     * 解析Kyou字符串
     * <p>
     * Kyou字符串是kyou自定义的方式，用来表示夹杂着字节的字符串。其中使用\作为转义符，\xx表示值为xx的字节。<br/>
     * 例：
     * <li>asdf - a s d f</li>
     * <li>as\00df - a s \0 d f</li>
     * <li>as\\df - a s \ d f</li>
     * </p>
     * 
     * @param str
     *            Kyou字符串
     * @param encoding
     *            编码
     * @return 该kyou字符串表示的字节流
     */
    public static byte[] parse(String str, Charset encoding) {
        if (str == null)
            throw new KyouException(KyouErr.Style.KyouStringSyntaxError, "<null>");
        if (encoding == null)
            throw new KyouException(KyouErr.Base.UnsupportedCharset, "<null>");

        // 如果str中不包含转义符则不需要处理，直接按encoding做编码并返回。
        if (!str.contains("\\"))
            return str.getBytes(encoding);

        // 缓存流
        KyouByteOutputStream s = new KyouByteOutputStream();
        // 在非转义状态下的文本
        StringBuilder builder = new StringBuilder();
        // 当前由转义符表示的字节
        int b = 0;
        // 解析器状态 0-无转义 1-当前是转义符后的第一个字符 2-当前是转义符后的第二个字符
        int state = 0;

        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++)
            // 判断是不是转义符
            if (chars[i] == '\\') {
                // 是转义符
                if (state == 0) {
                    // 启动转义符模式
                    s.write(builder.toString().getBytes(encoding));
                    builder = new StringBuilder();
                    b = 0;
                    state = 1;
                } else if (state == 1) {
                    // 连续两个\表示一个\字符
                    s.write("\\".getBytes(encoding));
                    state = 0;
                } else
                    // 语法错误
                    throw new KyouException(KyouErr.Style.KyouStringSyntaxError, "pos: " + i);
            } else {
                // 不是转义符
                if (state == 0)
                    // 当前不是转义模式，往builder后面缀普通的字符
                    builder.append(chars[i]);
                else if (state == 1) {
                    // 转义符后的第一位
                    b = parseHexChar(chars[i], i) << 4;

                    state = 2;
                } else {
                    // 转义符后的第二位
                    b |= parseHexChar(chars[i], i);

                    s.write(b);

                    state = 0;
                }
            }

        // 判断转义符状态。此时全部字符都解析完毕，state应当为0.
        if (state != 0)
            throw new KyouException(KyouErr.Style.KyouStringSyntaxError, "pos: " + chars.length);

        // 将最后一段文本冲到流里
        s.write(builder.toString().getBytes(encoding));

        // 将流中的东西返回
        return s.export();
    }

    /**
     * 工具函数 尝试将一个字符按照16进制解释成数字
     */
    private static int parseHexChar(char c, int pos) {
        if (c >= '0' && c <= '9')
            return c - '0';
        if (c >= 'a' && c <= 'f')
            return c - 'a' + 10;
        if (c >= 'A' && c <= 'F')
            return c - 'A' + 10;

        throw new KyouException(KyouErr.Style.KyouHexStringSyntaxError, "pos: " + pos);
    }
}
